<?php

class PlatformQQ extends PlatformBase
{
    private $gameId;

    private $updateMoney;
    /**
     * @var PlatformQQ
     */
    private static $instance;

    /**
     * Get the instance of PlatformQQ.
     *
     * @return PlatformQQ
     */
    public static function get()
    {
        if (!self::$instance) {
            self::$instance = new PlatformQQ();
        }
        return self::$instance;
    }

    private function __construct()
    {
        $this->configs = SzConfig::get()->loadAppConfig(ModuleSns::getSnsConfigFileName(self::PLATFORM_QQ), null, true);

        $this->appId = intval($this->configs['appId']['value']);
        $this->requestUrl = $this->configs['requestUrl']['value'];
        $this->appSecret = $this->configs['appSecret']['value'] . '&';//得到密钥的方式：在应用的appkey末尾加上一个字节的“&”，即appkey&
        $this->gameId = intval($this->configs['gameId']['value']);
        $this->updateMoney = [];
    }

    /**
     * 获取平台数据
     * @param $code
     * @return mixed|string
     */
    public static function getCodeMsg($code)
    {
        $config = SzConfig::get()->loadAppConfig(ModuleSns::getSnsConfigFileName(self::PLATFORM_QQ), null, true);
        $params = [
            'appid' => $config['appId']['value'],
            'secret' => $config['appSecret']['value'],
            'js_code' => $code,
            'grant_type' => 'authorization_code'
        ];

        $url = $config['requestUrl']['value'];
//        $url = 'https://api.q.qq.com/sns/jscode2session?';
        $try = 5;
        $result = '';
        while (--$try >= 0) {
            try {
                $result = SzUtility::postHttpRequest($url, $params);
                $try = 0;
            } catch (SzException $e) {

            }
        }
        $result = json_decode($result, true);
        return $result;
    }

    /**
     * 验证token
     *
     * @param string $platformId 平台ID
     * @param string $token 登录token
     * @throws SzException
     * @return bool
     */
    public function checkToken($platformId, $token)
    {
        $time = SzTime::getTime();
        $uri = '/openapi/apollo_verify_openid_openkey';
        $params = [
            'appid' => $this->appId,
            'gameid' => $this->gameId,
            'openkey' => $token,
            'openid' => $platformId,
            'rnd' => $this->genRnd(),
            'ts' => $time,
        ];
        $params['sig'] = $this->getSign($params, $uri);

        $url = $this->requestUrl . $uri;
        $try = 5;
        $result = '';
        while (--$try >= 0) {
            try {
                $result = SzUtility::postHttpRequest($url, $params);
                $try = 0;
            } catch (SzException $e) {

            }
        }
        $result = json_decode($result, true);
        if (isset($result['ret']) && $result['ret'] == 0) {
            return true;
        }
        throw new SzException(29202, [$uri, $result['ret'], $result['msg']]);
    }

    /**
     * 32位整形随机数，放在签名中避免碰撞
     * @return int  32位无符号整形
     */
    private function genRnd()
    {
        return rand(1, GameConst::SQL_INT_LIMITED);
    }


    /**
     * 获取签名
     * @param $params
     * @param $uri
     * @param string $method
     * @return string
     */
    private function getSign($params, $uri, $method = 'POST')
    {
        unset($params['sig']);
        ksort($params);
        $queryString = SzUtility::buildQueryString($params, false);
        $queryString = strtoupper($method) . '&' . rawurlencode($uri) . '&' . str_replace('~', '%7E', rawurlencode($queryString));

        return base64_encode(hash_hmac('sha1', $queryString, $this->appSecret, true));
    }

    /**
     *  获取手Q平台的所有道具信息
     * @param string $openId
     * @param string $openKey
     * @throws SzException
     * @return array
     */
    public function getGameItem($openId, $openKey)
    {
        $time = SzTime::getTime();
        $uri = '/openapi/apollo_game_item_proxy';
        $params = [
            'appid' => $this->appId,
            'cmd' => 1,
            'gameid' => $this->gameId,
            'mask' => 1,
            'openkey' => $openKey,
            'openid' => $openId,
            'rnd' => $this->genRnd(),
            'ts' => $time,
        ];
        $params['sig'] = $this->getSign($params, $uri);
        $url = $this->requestUrl . $uri;
        $try = 5;
        $result = '';
        while (--$try >= 0) {
            try {
                $result = SzUtility::postHttpRequest($url, $params);
                $try = 0;
            } catch (SzException $e) {

            }
        }
        $result = json_decode($result, true);
        SzLogger::get()->debug('getGameItem::' . json_encode($result));
        if (isset($result['ret']) && $result['ret'] == 0) {
            return $result['data']['itemList'];
        } else {
            throw new SzException(29202, [$uri, $result['ret'], $result['msg']]);
        }
    }

    /**
     *  消耗道具
     * @param string $openId
     * @param string $openKey
     * @param string $itemIds
     * @param string $itemNums
     * @param string $itemSeqs
     * @throws SzException
     * @return array
     */
    public function useGameItem($openId, $openKey, $itemIds, $itemNums, $itemSeqs)
    {
        $time = SzTime::getTime();
        $uri = '/openapi/apollo_game_item_proxy';
        $params = [
            'appid' => $this->appId,
            'cmd' => 2,
            'gameid' => $this->gameId,
            'itemids' => $itemIds,
            'itemnums' => $itemNums,
            'itemseqs' => $itemSeqs,
            'openkey' => $openKey,
            'openid' => $openId,
            'rnd' => $this->genRnd(),
            'ts' => $time,
        ];
        $params['sig'] = $this->getSign($params, $uri);
        $url = $this->requestUrl . $uri;
        $try = 5;
        $result = '';
        while (--$try >= 0) {
            try {
                $result = SzUtility::postHttpRequest($url, $params);
                $try = 0;
            } catch (SzException $e) {

            }
        }
        SzLogger::get()->debug('useGameItem::params::' . json_encode($params));
        $result = json_decode($result, true);
        SzLogger::get()->debug('useGameItem::' . json_encode($result));
        if (isset($result['ret']) && $result['ret'] == 0) {
            return $result['data']['succList'];
        } else {
            throw new SzException(29202, [$uri, $result['ret'], $result['msg']]);
        }
    }

    /**
     *  获取用户真实货币
     * @param int $userId
     * @throws SzException
     * @return int
     */
    public function getMoney($userId)
    {
        $openId = UserService::get()->getPlatformId($userId, 'qq');
        $openKey = UserService::get()->getPlatformToken($userId);

        $result = $this->getGameItem($openId, $openKey);
        SzLogger::get()->debug('getMoney::' . json_encode($result));
        if (count($result) != 0) {
            $itemList = [];
            $itemReq = [];
            foreach ($result as $item) {
                if (isset($itemList[$item['id']])) {
                    $itemList[$item['id']] += $item['num'];
                } else {
                    $itemList[$item['id']] = $item['num'];
                    $itemReq[] = $this->genRnd();
                }
            }
            $result = $this->useGameItem($openId, $openKey, implode('_', array_keys($itemList)), implode('_', $itemList), implode('_', $itemReq));

            $money = 0;
            $commodityValue = 0;
            foreach ($result as $item) {
                if (isset($itemList[$item['id']])) {
                    $gamePaySetting = $this->getGamePaySettingByQQItemId($item['id']);
                    if (is_null($gamePaySetting)) {
                        continue;
                    }
                    $money += $gamePaySetting['count'] * $itemList[$item['id']];
                    $commodityValue += $gamePaySetting['price'] * ModulePaymentConst::SINGLE_COMMODITY_VALUE_OF_PAY * $itemList[$item['id']];
                }
            }
            if ($money > 0) {
                /**
                 * @var ModulePaymentVo $payment
                 */
                $payment = SzPersister::get()->getVo(null, ModulePaymentConst::ORM_NAME_PAYMENT, $userId);
                $payment->updatePurchasedMoney($money);
                $payment->updateCommodityValue($commodityValue);
                SzPersister::get()->setVo($payment);
            }
        }
        return parent::getMoney($userId);
    }

    /**
     * 获取qqId对应的充值项
     * @param $qqItemId
     * @return null
     */
    private function getGamePaySettingByQQItemId($qqItemId)
    {
        $gamePaySettings = SzConfig::get()->loadAppConfig(GameConst::CONFIG_GAME_PAY, null, true);
        foreach ($gamePaySettings as $setting) {
            if ($setting['qqId'] == $qqItemId) {
                return $setting;
            }
        }
        return null;
    }
}